import 'reflect-metadata'
import express, { Express, Request as ExpressRequest, Response as ExpressResponse, NextFunction } from 'express'
import { Logger } from './logger'
import path from 'path'
import {
  INJECTED_TOKEN,
  DESIGN_PARAMTYPES,
  defineModule,
  RequestMethod,
  GlobalHttpExceptionFilter,
  ForbiddenException,
} from '../common'
import { APP_FILTER, DECORATOR_FACTORY, APP_PIPE, FORBIDDEN_RESOURCE, APP_GUARD, APP_INTERCEPTOR } from './constants'
import { Reflector } from './reflector'
import {
  Observable, from, mergeMap, of,
} from 'rxjs'

// 扩展 Express.Request 接口
declare global {
  namespace Express {
    interface Request {
      user?: {
        name: string;
        role: string;
      }
    }
  }
}

/**
 * 创建 nest app 应用\
 */
export class NestApplication {
  private readonly app: Express = express() // 创建 express app 应用实例 提供 HTTP 相关服务
  // 保存所有的提供者 key 是 token令牌 值是提供者实例(类提供者实例)或提供者的值(工厂函数提供者或值提供者)
  private readonly providerInstances = new Map()
  // 记录每个模块里面有哪些 provider 提供者的 token
  // key是模块类 值是一个 Set 里面保存了该模块对应的提供者token
  private readonly moduleProviders = new Map()
  private readonly globalProviders = new Set() // 记录全局模块中暴露的提供者 所有的模块内都可以使用的提供者的 token
  private readonly middlewares = [] // 保存所有的中间件 可能是一个函数 也可能是一个类 也可能是一个类的实例
  private readonly excludedRoutes = [] // 记录需要排除的路由
  private readonly defaultGlobalHttpExceptionFilter = new GlobalHttpExceptionFilter() // 默认的全局异常过滤器
  private readonly globalHttpExceptionFilters = [] // 用于保存自定义全局异常过滤器的数组
  private readonly globalPipes = [] // 用于保存全局管道
  private readonly globalGuards = [] // 用于保存全局守卫
  private readonly globalInterceptors = [] // 用于保存全局拦截器
  // 特殊的全局提供者映射表 Map(4) { APP_GUARD => Map(), APP_PIPE => Map(), APP_FILTER => Map(), APP_INTERCEPTOR => Map() }
  private readonly globalProviderMap = new Map([
    [APP_GUARD, new Map()], // 全局守卫
    [APP_PIPE, new Map()], // 全局管道
    [APP_FILTER, new Map()], // 全局过滤器
    [APP_INTERCEPTOR, new Map()], // 全局拦截器
  ])
  /**
   * 使用根模块来初始化项目
   * @param module 根模块
   */
  constructor(protected readonly module) {
    // 使用 express 内置的中间件来解析请求体中的JSON数据
    this.app.use(express.json())
    // 使用 express 内置的中间件来解析请求体中的URL application/x-www-form-urlencoded 编码数据
    this.app.use(express.urlencoded())

    this.app.use((req, res, next) => {
      req.user = {
        name: 'zs',
        role: 'admin'
      }
      next()
    })
  }

  // 添加全局管道
  useGlobalPipes(...pipes) {
    this.globalPipes.push(...pipes)
  }

  // 添加自定义全局异常过滤器
  useGlobalFilters(...filters) {
    // 给自定义全局异常过滤器类与根模块进行关联
    defineModule(this.module, filters.filter(filter => filter instanceof Function))
    this.globalHttpExceptionFilters.push(...filters)
  }

  // 初始化所有的中间件
  private async initMiddlewares() {
    await this.module.prototype.configure?.(this) // 调用根模块类的 configure 方法
  }

  // 保存中间件
  apply(...middleware) {
    // 给每个中间件都添加一个 nestModule 元数据 元数据的值指向根模块 
    defineModule(this.module, middleware)
    this.middlewares.push(...middleware) // 将传递的中间件保存起来
    return this
  }

  // 如果是类中间件则实例化类中间件并返回 如果是函数式中间件或类中间件实例则原样返回
  getMiddlewareInstance(middleware) {
    const dependencies = this.resolveDependencies(middleware) // 解析类中间件的依赖
    return new middleware(...dependencies) // 实例化类中间件并返回
  }

  // 应用中间件
  forRoutes(...routes) {
    for (const route of routes) { // 遍历每一个路径信息请求方式参数 中间件将基于参数应用到相应的位置
      for (const middleare of this.middlewares) { // 遍历每一个中间件
        // 将 route 格式化为标准的对象 一个是路径 一个是请求方式如 GET POST
        const { routePath, routeMethod } = this.normalizeRouteInfo(route)
        this.app.use(routePath, (req, res, next) => {
          if (this.isExcluded(req.originalUrl, req.method)) { // 根据HTTP请求的路径和请求方式来决定是否执行中间件
            return next()
          }
          if (routeMethod === RequestMethod.ALL || routeMethod === req.method) {
            // 此处 middleware 可能是一个类  也可能是一个函数
            if (Reflect.has(middleare.prototype, 'use')) { // middleware 是一个类中间件
              const middlewareInstance = this.getMiddlewareInstance(middleare)
              middlewareInstance.use(req, res, next)
            } else { // middleware 是一个函数中间件
              middleare(req, res, next)
            }
          } else {
            next()
          }
        })
      }
    }
    // 执行一次 forRoutes 会让已经保存的中间件函数执行的同时把以前保存的中间件函数清空
    this.middlewares.length = 0
    // 执行一次 forRoutes 会让排除的路径清空
    this.excludedRoutes.length = 0
    return this
  }

  // 记录中间件需要排除的路径和请求方式
  exclude(...routesInfo) {
    routesInfo = routesInfo.map(this.normalizeRouteInfo) // 映射排除路径
    this.excludedRoutes.push(...routesInfo) // 保存需要排除的路径+请求方式
    return this
  }

  // 根据请求路径和请求方式来判断是否需要排除中间件
  isExcluded(reqPath: string, reqMethod: string) {
    return this.excludedRoutes.some(routeInfo => {
      const { routePath, routeMethod } = routeInfo
      // 排除的路径相同并且（ 排除的方法是 all 或排除的方法与保存的相同 ）则排除路径
      return routePath === reqPath && (routeMethod === RequestMethod.ALL || routeMethod === reqMethod)
    })
  }

  // 将 route 转换一个对象 routePath是中间件应用的路径 routeMethod是中间件应用的方式
  private normalizeRouteInfo(route) {
    let routePath = ''
    let routeMethod = RequestMethod.ALL // 默认中间件支持所有的请求方式
    if (typeof route === 'string') { // 传的是一个路径
      routePath = route
    } else if (Reflect.has(route, 'path')) { // 传的是一个路径+请求方式对象
      routePath = route.path
      routeMethod = route.method ?? RequestMethod.ALL // 如果没有传递请求方式则默认使用 RequestMethod.ALL
    } else if (route instanceof Function) { // 是一个控制器类
      routePath = Reflect.getMetadata('prefix', route) // 取出控制器前缀路径作为中间件应用的路径条件
    }
    // 处理 forRoutes 时传递的路径没有加 '/' 情况 
    // 比如 'user' 转换后是 '/user'  而 '/user' 转换后还是 '/user' 
    routePath = path.posix.join('/', routePath)
    return { routePath, routeMethod }
  }

  /**
   * 注册一些由 NestJS 内部自动提供并注册的提供者
   */
  private addDefaultProviders() {
    // 注册nesjts默认的提供者 比如 Reflector
    this.addProvider(Reflector, this.module, true)
  }

  // 初始化所有的提供者
  async initProviders() {
    this.addDefaultProviders()
    // 获取根模块导入的模块类组成的数组 如果没有导入则为空数组
    const imports = Reflect.getMetadata('imports', this.module) ?? []
    for (const importModule of imports) { // 遍历数组 获取每个模块类
      let importedModule = importModule
      if (importModule instanceof Promise) { // 异步动态模块
        importedModule = await importModule // 获取 Promise 中的动态模块
      }
      if (Reflect.has(importedModule, 'module')) { // 动态模块
        const { module, imports = [], providers = [], controllers = [], exports = [] } = importedModule
        const oldImports = Reflect.getMetadata('imports', module) ?? [] // 获取模块类 imports 元数据
        const oldControllers = Reflect.getMetadata('controllers', module) ?? [] // 获取模块类 controllers 元数据
        const oldProviders = Reflect.getMetadata('providers', module) ?? [] // 获取模块类 providers 元数据
        const oldExports = Reflect.getMetadata('exports', module) ?? [] // 获取模块类 exports 元数据
        const newImports = [...oldImports, ...imports] // 合并 @Module 装饰器中 imports
        const newControllers = [...oldControllers, ...controllers] // 合并 @Module 装饰器中 controllers
        const newProviders = [...oldProviders, ...providers] // 合并 @Module 装饰器中 providers 元数据
        const newExports = [...oldExports, ...exports] // 合并 @Module 装饰器中 exports 元数据
        // 重新给模块类添加 imports controllers providers exports 元数据
        Reflect.defineMetadata('imports', newImports, module)
        Reflect.defineMetadata('controllers', newControllers, module)
        defineModule(module, newControllers) // 将每个控制器与动态模块类建立关系 
        Reflect.defineMetadata('providers', newProviders, module)
        defineModule(module, newProviders) // 将每个提供者与动态模块类建立关系
        Reflect.defineMetadata('exports', newExports, module)
        this.registerProvidersFromModule(module, this.module) // 动态(全局)模块 根模块 ( 处理提供者 + 该模块对应的路由处理函数映射路由 )
      } else {
        this.registerProvidersFromModule(importedModule, this.module) // 获取导入模块暴露的提供者 ( 处理提供者 + 该模块对应的路由处理函数映射路由 )
      }
    }
    // 获取根模块类 providers 元数据 值是根模块类自身的提供者 ( 如果没有提供者则默认是一个空数组 )
    const providers = Reflect.getMetadata('providers', this.module) ?? []
    for (const provider of providers) { // 遍历根模块提供者数组 获取每个根模块提供者
      this.processProvider(provider, this.module) // 将根模块的提供者添加到 Map 中
    }
  }

  /**
   * 将特殊的提供者和普通的提供者映射关系分开保存
   */
  private processProvider(provider, module) {
    if (this.globalProviderMap.has(provider.provide)) { // 判断是不是一个特殊的全局提供者 比如全局拦截器 全局管道 全局异常过滤器
      const instanceMap = this.globalProviderMap.get(provider.provide) // 获取特殊的提供者
      const { useClass } = provider // 获取特殊提供者的类  此处简单处理
      if (!instanceMap.has(useClass)) {
        const instance = new useClass(...this.resolveDependencies(useClass)) // 获取特殊的提供者实例
        instanceMap.set(useClass, instance) // 将类 + 类实例保存到相应的 Map 中
      }
    } else {
      this.addProvider(provider, module)
    }
  }

  // 通过模块类树注册提供者 + 映射模块中的路由函数与路由关系
  // 根模块引入了 A模块 和 B模块 并且 A模块暴露了 AService 提供者 该提供者对 根模块 + A模块都是可见的
  // module是导入模块 ...parentModules 是祖先模块 需要把导入模块中暴露的提供者关联到导入模块+祖先模块
  // 如果 module导入模块暴露的是一个模块 则需要处理该模块的提供者 递归
  private registerProvidersFromModule(module, ...parentModules) {
    // 从模块类中取出 global 元数据 ( true代表全局模块 如果取不到元数据对应的值则该模块类不是一个全局模块 )
    const global = Reflect.getMetadata('global', module)
    // 获取导入模块类的 providers 元数据 值是导入模块类自己的提供者组成的数组 ( 如果没有则默认是一个空数组 )
    const importedProviders = Reflect.getMetadata('providers', module) ?? []
    // 获取导入模块类的 exports 元数据 值是导入模块暴露的提供者和模块 ( 如果没有则默认是一个空数组 )
    const exports = Reflect.getMetadata('exports', module) ?? []
    for (const exportToken of exports) { // 遍历获取导入模块类暴露的每个提供者或模块
      if (this.isModule(exportToken)) { // 导入模块暴露的是一个模块
        this.registerProvidersFromModule(exportToken, module, ...parentModules) // 递归 暴露的是一个模块
      } else { // 导入模块暴露的是一个提供者
        // 从 导入模块 的提供者中基于 exportToken 获取 导入模块 对应的提供者
        const provider = importedProviders.find(provider => {
          return provider === exportToken || provider.provide === exportToken
        })
        if (provider) { // 导入模块自身提供者存在
          [module, ...parentModules].forEach((m) => {
            this.addProvider(provider, m, global) // 导入模块暴露的提供者保存到 Map 中
          })
        }
      }
    }
    this.initController(module) // 处理 module 该模块的控制器
  }

  // 判断 exportToken 是一个模块类
  private isModule(exportToken) {
    return exportToken instanceof Function && Reflect.getMetadata('isModule', exportToken)
  }

  // 处理提供者 将提供者token令牌与所属模块关联保存起来 ( 模块之间的提供者隔离 ) + 全局模块暴露的提供者在所有模块或提供者内都可用
  // 保存提供者 ( 类提供者保存实例 值提供者保存值 工厂函数提供者保存工厂函数调用后的值 )
  addProvider(provider, module, global = false) {
    // 从 this.moduleProviders 里面获取 module 模块类对应的 Set 如果没有则默认为一个空 Set
    // 如果是全局模块则获取 this.globalProviders 否则获取 this.moduleProviders 中对应的 Set
    const providers = global ? this.globalProviders : (this.moduleProviders.get(module) || new Set())
    // module 如果不是全局模块 并且 this.moduleProviders 中没有 module 模块类关联的 Set
    if (!global && !this.moduleProviders.has(module)) {
      // 把 模块类 和关联的 Set 提供者 token令牌 关联起来并保存到 this.moduleProviders 中
      this.moduleProviders.set(module, providers)
    }

    let injectToken = provider.provide ?? provider // 获取提供者的 token令牌
    if (this.providerInstances.has(injectToken)) { // this.providerInstances 提供者实例池中已经存在则不需要再次创建提供者
      return providers.add(injectToken) // 直接在对应 Set 里面保存这个 token令牌
    }

    if (provider.provide && provider.useClass) { // 类提供者
      const clazz = provider.useClass // 获取类提供者
      const dependencies = this.resolveDependencies(clazz) // 获取类提供者的依赖
      const classInstance = new clazz(...dependencies) // 实例化类提供者
      this.providerInstances.set(provider.provide, classInstance) // token令牌 和 类实例 以 key/value 的形式保存到 Map 中
      providers.add(provider.provide) // 将 token令牌 保存到 Set 中
    } else if (provider.provide && provider.useValue) { // 值提供者
      this.providerInstances.set(provider.provide, provider.useValue) // token令牌 和 值 以 key/value 的形式保存到 Map 中
      providers.add(provider.provide) // 将 token令牌 保存到 Set 中
    } else if (provider.provide && provider.useFactory) { // 工厂提供者
      const inject = provider.inject ?? [] // 工厂提供者依赖的提供者组成的数组 默认是一个空数组
      // 基于 token令牌 获取对应的提供者
      const injectValue = inject.map((injectToken) => this.getProviderByToken(injectToken, module))
      const value = provider.useFactory(...injectValue) // 调用工厂函数 获取返回的值
      this.providerInstances.set(provider.provide, value) // token令牌 和 值 以 key/value 的形式保存到 Map 中
      providers.add(provider.provide) // 将 token令牌 保存到 Set 中
    } else { // 类提供者 简写形式
      const dependencies = this.resolveDependencies(provider) // 获取类提供者的依赖
      const classInstance = new provider(...dependencies) // 实例化类提供者
      this.providerInstances.set(provider, classInstance) // 类提供者 和 类提供者实例 以 key/value 的形式保存到 Map 中
      providers.add(provider) // 将 token令牌 保存到 Set 中
    }
  }

  use(middleware) {
    this.app.use(middleware) // 使用中间件
  }

  /**
   * 基于 token 令牌返回对应的提供者
   * @param injectToken 提供者的 token 令牌
   * @returns 提供者
   */
  private getProviderByToken = (injectToken, module) => {
    // 判断 module 模块类有没有对应的 injectToken
    if (this.moduleProviders.get(module)?.has(injectToken) || this.globalProviders.has(injectToken)) {
      // 有 则从 this.providerInstances 中基于 injectToken 取出对应的提供者 或 全局模块中的提供者
      return this.providerInstances.get(injectToken)
    } else {
      return null
    }
  }

  /**
   * 处理控制器类构造函数或提供者类构造函数或类中间件构造函数参数的 token 令牌依赖
   * @param Clazz 控制器类或提供者类
   */
  private resolveDependencies(Clazz) {
    // 从类上取出 INJECTED_TOKEN 元数据 ( 有可能没有任何依赖则使用空数组作为默认值 )
    // [ <1 empty item>, 'StringToken' ]
    const injectedTokens = Reflect.getMetadata(INJECTED_TOKEN, Clazz) ?? []
    // design:paramtypes 获取构造函数参数类型组成的数组 如果参数没有写类型则类型默认为 Object 
    // [ [class LoggerService], [class useValueService] ] 
    // 不是每一个控制器类或者提供者类都有 design:paramtypes 元数据对应的值 当没有值的时候给一个空数组作为默认值 ( 当类没有构造函数时元数据值为 undefined )
    const constructorParams = Reflect.getMetadata(DESIGN_PARAMTYPES, Clazz) ?? []
    // 遍历 design:paramtypes 构造函数参数类型组成的数组 param参数类型 index参数索引
    return constructorParams.map((param, index) => {
      // 从提供者或控制器类中获取 nestModule 元数据 其中 值是对应的 模块类
      const module = Reflect.getMetadata('nestModule', Clazz)
      // 优先基于索引从 injectedTokens 中取 token 令牌
      // 其次使用参数类型作为 token 令牌
      return this.getProviderByToken(injectedTokens[index] ?? param, module)
    })
  }

  // 将守卫实例化 
  private getGuardInstance(guard) {
    if (typeof guard === 'function') { // 类守卫
      const dependencies = this.resolveDependencies(guard) // 获取类守卫的依赖
      return new guard(...dependencies) // 实例化守卫时传递相关依赖
    }
    return guard // 不是类守卫 直接原样返回
  }

  // 执行守卫 接收2个参数 守卫组成的数组 上下文对象
  async callGuards(guards, context) {
    for (const guard of guards) {
      const guardInstance = this.getGuardInstance(guard) // 获取守卫实例
      // 调用管道实例的 canActivate 方法传递 context 上下文
      const canActivate = await guardInstance.canActivate(context)
      if (!canActivate) { // 守卫没有通过 不允许执行路由处理函数 抛出错误由异常过滤器处理
        throw new ForbiddenException(FORBIDDEN_RESOURCE)
      }
    }
  }

  /**
   * 配置初始化工作\
   * 根据模块 取出模块里面的所有控制器 然后映射路径
   */
  async initController(module) {
    // 获取根模块类中 controllers 元数据 ( 是一个包含相关控制器类的数组 ) 如果元数据的值没有则是 []
    const controllers = Reflect.getMetadata('controllers', module) || []
    Logger.log(`AppModule dependencies initialized `, 'InstanceLoader')
    // 遍历根模块中的所有控制器类
    for (const Controller of controllers) {
      const dependencies = this.resolveDependencies(Controller) // 解析出控制器类的依赖
      const controller = new Controller(...dependencies) // 实例化控制器类
      // 获取控制器类上的路径前缀 如果没有就使用 '/' 作为默认值
      const prefix = Reflect.getMetadata('prefix', Controller) || '/'
      Logger.log(`${Controller.name} {${prefix}}`, 'RoutesResolver')
      const controllerPrototype = Reflect.getPrototypeOf(controller) // 获取控制器实例的原型
      const controllerFilters = Reflect.getMetadata('filters', Controller) ?? [] // 获取控制器上绑定的异常过滤器数组
      defineModule(module, controllerFilters.filter(f => f instanceof Function)) // 异常过滤器类与根模块关联
      const controllerPipes = Reflect.getMetadata('pipes', Controller) ?? [] // 获取控制器类上 pipes 元数据 值是管道组成的数组
      const controllerGuards = Reflect.getMetadata('guards', Controller) ?? [] // 获取控制器类上的 guards 元数据 值是守卫组成的数组
      const controllerInterceptors = Reflect.getMetadata('interceptors', Controller) ?? [] // 获取控制器类上的 intercetpros 元数据 值是拦截器组成的数组
      // 遍历控制器原型对象上( 不包含 Symbol 作为 key )的每个自有属性组成的数组 ['getHello', 'handleQuery', 'handleHandlers'...]
      for (const methodName of Object.getOwnPropertyNames(controllerPrototype)) {
        const method = controllerPrototype[methodName] // 获取控制器类原型上的方法 可能该方法是一个路由处理函数
        const httpMethod = Reflect.getMetadata('method', method) // 获取方法上的 method 元数据
        const pathMetadata = Reflect.getMetadata('path', method) // 获取方法上的 path 元数据
        const redirectUrl = Reflect.getMetadata('redirectUrl', method) // 获取方法上的重定向路径
        const redirectStatusCode = Reflect.getMetadata('redirectStatusCode', method) // 获取重定向状态码
        const statusCode = Reflect.getMetadata('statusCode', method) // 获取响应状态码
        const headers = Reflect.getMetadata('headers', method) ?? [] // 获取需要设置的响应头 默认值为空数组
        const methodFilters = Reflect.getMetadata('filters', method) ?? [] // 获取路由处理函数上的异常过滤器
        defineModule(module, methodFilters.filter(f => f instanceof Function)) // 异常过滤器类与根模块关联
        const methodPipes = Reflect.getMetadata('pipes', method) ?? [] // 获取路由处理函数上 pipes 元数据 值是管道组成的数组
        const pipes = [...controllerPipes, ...methodPipes] // 合并控制器+路由处理函数上的管道
        const methodGuards = Reflect.getMetadata('guards', method) ?? [] // 获取路由处理函数上的 guards 元数据 值是守卫组成的数组
        const guards = [...this.globalGuards, ...controllerGuards, ...methodGuards] //全局管道 + 控制器上的守卫 + 路由处理函数上的守卫 合并
        const methodInterceptors = Reflect.getMetadata('interceptors', method) ?? [] // 获取路由处理函数上的 interceptors 元数据 值是拦截器组成的数组
        const interceptors = [...this.globalInterceptors, ...controllerInterceptors, ...methodInterceptors] // 合并 全局 + 控制器类 + 方法上 的拦截器数据
        if (!httpMethod) continue; // 如果方法上没有 method 元数据 代表当前方法不是一个路由处理函数
        const routePath = path.posix.join('/', prefix, pathMetadata) // 拼接HTTP请求路径
        // 监听HTTP请求 this.app.get(请求路径)
        this.app[httpMethod.toLowerCase()](routePath, async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
          const ctx = {
            switchToHttp: () => ({
              getRequest: () => req,
              getResponse: () => res,
              getNext: () => next
            })
          }
          const context = {
            ...ctx,
            getClass: () => Controller,
            getHandler: () => method
          }
          try { // 将路由处理函数包裹在 try catch 中执行
            // 先执行 guards 守卫
            await this.callGuards(guards, context)
            const args = await this.resolveParams(controller, methodName, ctx, pipes) // 获取路由处理函数的参数组成的数组
            this.callInterceptors(controller, method, args, interceptors, context, ctx, pipes) // 拦截器相关
              .subscribe({
                next: result => {
                  // console.log('subscribe next result ', result)
                  // 判断是否需要重定向
                  if (typeof redirectUrl === 'string') {
                    if (typeof result?.url === 'string') { // 优先使用 HttpRedirectResponse 接口的返回值
                      return res.redirect(result.statusCode, result.url)
                    } else { // 其次使用 @Redirect() 装饰器指定的重定向路径和响应状态码
                      return res.redirect(redirectStatusCode, redirectUrl)
                    }
                  }
                  // 判断是否手动设置了响应状态码
                  if (statusCode) {
                    res.statusCode = statusCode // 使用手动设置的响应状态码
                  } else if (httpMethod === 'POST') { // 默认情况下 POST 请求成功响应的状态码为 201
                    res.statusCode = 201
                  }

                  // 使用了 @Header 装饰器设置响应头
                  headers.forEach(({ name, value }) => {
                    res.setHeader(name, value) // 根据响应头数组设置响应头
                  })

                  // 判断当前路由处理函数参数中是否使用了 @Res() 或 @Response() 或 @Next() 装饰器
                  const responseMetadata = this.getResponseMetadata(controller, methodName)
                  if (!responseMetadata || responseMetadata?.data?.passthrough) {
                    // 没有使用 @Res() 或 @Response() 装饰器 或者 使用了 @Res() 或 @Response() 装饰器并配置了 {passthrough: true}
                    // 默认情况下 nestjs 会使用 express 中间件里面的 res.send 方法来返回响应结果
                    // res.send 方法支持 字符串 对象 数组 数字 布尔值 null undefined 
                    res.send(result)
                  }
                },
                error: error => { // 处理拦截器中的错误情况
                  this.callExceptionFilters(error, ctx, methodFilters, controllerFilters) // 根据错误对象 交给异常过滤器来处理
                }
              })
          } catch (error) { // 捕获到路由处理函数中抛出的错误
            this.callExceptionFilters(error, ctx, methodFilters, controllerFilters) // 根据错误对象 交给异常过滤器来处理
          }

        })
        Logger.log(`Mapped ${routePath}, ${httpMethod} route`, 'RoutesResolver')
      }
    }
    Logger.log(`Nest application successfully started`, 'InstanceLoader')
  }

  /**
   * 获取拦截器实例
   * @param interceptor 拦截器
   */
  getInterceptorInstance(interceptor) {
    if (typeof interceptor === 'function') {
      const dependencies = this.resolveDependencies(interceptor) // 获取拦截器类的依赖
      return new interceptor(...dependencies) // 实例化拦截器
    }
    return interceptor
  }

  /**
   * 执行拦截器
   * @param controller 控制类
   * @param method 路由处理函数
   * @param args 路由处理函数上的参数组成的数组
   * @param interceptors 拦截器数据
   * @param context 上下文对象 还可以获取控制器类 + 方法
   * @param ctx 上下文对象 只能获取 请求对象 + 响应对象 + next
   * @param pipes 管道组成的数组
   */
  callInterceptors(controller, method, args, interceptors, context, ctx, pipes) {
    // 每调用一次 nextFn 就执行了一个拦截器 
    const nextFn = (i = 0): Observable<any> => {
      if (i === interceptors.length) { // 前置拦截器执行完毕
        const result = method.apply(controller, args) // 执行路由处理函数
        return result instanceof Promise ? from(result) : of(result) // 将路由处理函数的返回值 包装成可观察对象
      }
      // 获取当前拦截器的实例
      const interceptorInstance = this.getInterceptorInstance(interceptors[i])
      const result = interceptorInstance.intercept(context, { handle: () => nextFn(i + 1) })
      // 异步拦截器则使用 mergeMap 将 Observable<Observable<路由处理函数返回值>> 变成 Observable<路由处理函数返回值>
      // 同步拦截器则使用 mergeMap 将 Observable<路由处理函数返回值> 变成 Observable<路由处理函数返回值> 始终保持一层 Observable
      return from(result).pipe(mergeMap(val => val instanceof Observable ? val : of(val)))
    }
    return nextFn()
  }

  getFilterInstance(filter) {
    if (filter instanceof Function) {
      const dependencies = this.resolveDependencies(filter)
      return new filter(...dependencies)
    }
    return filter
  }

  // 默认情况下 内置的全局异常过滤器根据错误对象的类型决定如何处理异常
  private callExceptionFilters(error, host, methodFilters, controllerFilters) {
    // 将所有的异常过滤器都保存到 allFilters 数组中
    const allFilters = [
      ...methodFilters, // 优先使用路由处理函数上的自定义异常过滤器
      ...controllerFilters, // 其次使用控制器类上的自定义异常过滤器
      ...this.globalHttpExceptionFilters, // 再其次使用自定义全局异常过滤器
      this.defaultGlobalHttpExceptionFilter // 最后使用默认的全局异常过滤器
    ]
    for (const filter of allFilters) { // 遍历异常过滤器的数组 获取每一个异常过滤器 每个过滤器只处理和自己相关的异常情况
      let filterInstance = this.getFilterInstance(filter) // 获取异常过滤器类的实例 如果是实例则直接原样返回
      const exceptions = Reflect.getMetadata('catch', filterInstance.constructor) ?? [] // 获取自定义异常过滤器实例构造函数中 catch 元数据
      if (exceptions.length === 0 || exceptions.some(exception => error instanceof exception)) {
        // 表示当前异常过滤器需要处理所有情况的异常 或者当前错误情况包含在 exceptions 数组中 需要处理该异常情况
        // 正常情况 如果异常过滤器捕获到异常需要处理并结束该异常 而不能交给后续的异常过滤器
        filterInstance.catch(error, host)
        break;
      }
    }
  }

  /**
   * 用于判断路由处理函数参数中是否使用了 @Res装饰器 或 @Response装饰器 或 @Next装饰器
   * @param controller 控制器类实例
   * @param methodName 方法名称
   */
  private getResponseMetadata(controller, methodName) {
    const paramsMetaData = Reflect.getMetadata(`params`, controller, methodName) ?? []
    return paramsMetaData.filter(Boolean).find(
      param =>
        param.key === 'Response' ||
        param.key === 'Res' ||
        param.key === 'Next'
    )
  }

  /**
   * 解析控制器类原型路由处理函数的参数
   * @param instance 控制器类实例
   * @param methodName 方法名
   * @param ctx 上下文对象
   * @param pipes 控制器类上的管道+路由处理函数上的管道组成的数组
   */
  private async resolveParams(instance, methodName: string, ctx, pipes) {
    const { getRequest, getResponse, getNext } = ctx.switchToHttp() // 获取 http 上下文对象
    const req: ExpressRequest = getRequest()
    const res: ExpressResponse = getResponse()
    const next: NextFunction = getNext()
    // 从控制器类实例上取 methodName 的 `params` 元数据发现没有 则从控制器类实例的原型上取 metadataName 的 `params` 元数据
    const paramsMetaData = Reflect.getMetadata(`params`, instance, methodName) ?? []
    return Promise.all(paramsMetaData.map(async paramMetaData => {
      // key 是参数装饰器名称 比如 'Request' 'Req' 'Query'
      // data 是使用参数装饰器工厂函数传递的参数
      // factory 自定义参数装饰器
      // paramPipes 参数装饰器上的管道 
      const { key, data, factory, pipes: paramPipes, metatype } = paramMetaData
      let value;
      switch (key) {
        case 'Request':
        case 'Req':
          value = req;
          break;
        case 'Query':
          value = data ? req.query[data] : req.query
          break;
        case 'Headers':
          value = data ? req.headers[data] : req.headers
          break;
        case 'Session':
          value = data ? req.session[data] : req.session
          break;
        case 'Ip':
          value = req.ip
          break;
        case 'Param':
          value = data ? req.params[data] : req.params
          break;
        case 'Body':
          // 依赖 express 中的 express.json() 处理请求体中的 JSON 数据
          // 依赖 express 中的 express.urlencoded() 处理请求体中的 URL application/x-www-form-urlencoded 编码数据
          value = data ? req.body[data] : req.body
          break;
        // 使用 @Res() 或 @Response() 装饰器时需要手动管理如何响应 如果不手动响应则会被挂起
        case 'Response':
        case 'Res':
          value = res
          break;
        case DECORATOR_FACTORY: // 自定义参数装饰器
          value = factory(data, ctx);
          break;
        default:
          value = null
          break
      }
      // 执行管道操作 先执行全局管道 再执行控制器类上的所有管道 再执行路由处理函数上的管道 最后执行参数上的管道
      for (const pipe of [...this.globalPipes, ...pipes, ...paramPipes]) {
        const pipeInstance = this.getPipeInstance(pipe)
        // 生成管道类transform方法的第二个参数ArgumentMetadata接口元数据
        const type = key.toLowerCase() === DECORATOR_FACTORY ? 'custom' : key.toLowerCase()
        value = await pipeInstance.transform(value, { type, data, metatype })
      }
      return value
    }))
  }

  // 将管道实例化
  private getPipeInstance(pipe) {
    if (typeof pipe === 'function') { // 管道类
      const dependencies = this.resolveDependencies(pipe) // todo 此时模块还没有与管道关联
      return new pipe(...dependencies)// 实例化管道类
    }
    return pipe // 原样返回管道
  }

  // 处理自定义全局异常过滤器类需要依赖注入情况
  async initGlobalFilters() {
    const providers = Reflect.getMetadata('providers', this.module) ?? [] // 从根模块中取出 providers 元数据
    for (const provider of providers) { // 遍历根模块上的所有提供者
      if (provider.provide === APP_FILTER) { // 自定义的全局异常过滤器类 
        // 从根模块上根据 token 信息获取 自定义全局异常过滤器实例
        const providerInstance = this.getProviderByToken(APP_FILTER, this.module)
        this.useGlobalFilters(providerInstance) // 保存自定义全局异常过滤器实例
      }
    }
  }

  // 获取全局管道实例 保存到 this.globalPipes 数组中
  async initGlobalPipes() {
    const providers = Reflect.getMetadata('providers', this.module) ?? [] // 从根模块中取 providers 元数据
    for (const provider of providers) { // 遍历根模块上的所有提供者
      if (provider.provide === APP_PIPE) { // 表示当前是一个全局过滤器
        const providerInstance = this.getProviderByToken(APP_PIPE, this.module) // 从根模块中获取 token令牌 对应的服务
        this.useGlobalPipes(providerInstance) // 保存到全局管道中
      }
    }
  }

  // 从根模块中获取提供者里面的全局守卫 保存到 this.
  async initGlobalGuards() {
    const providers = Reflect.getMetadata('providers', this.module) ?? [] // 从根模块中取 providers 元数据
    for (const provider of providers) { // 遍历根模块上的所有提供者
      if (provider.provide === APP_GUARD) { // 表示当前是一个守卫
        const providerInstance = this.getProviderByToken(APP_GUARD, this.module) // 从根模块中获取 token令牌 对应的服务
        this.useGlobalGuard(providerInstance) // 保存到 this.golbalGuards 全局守卫中
      }
    }
  }

  // 将全局守卫保存到 this.globalGuards 中
  useGlobalGuard(...guards) {
    this.globalGuards.push(...guards)
  }


  // 将全局拦截器添加到 this.globalInterceptors 中
  useGlobalInterceptors(...interceptors) {
    this.globalInterceptors.push(...interceptors)
  }

  // 处理特殊的全局提供者
  private initGlobalProviders() {
    for (const [ provide, instanceMap ] of this.globalProviderMap) {
      switch(provide) {
        case APP_INTERCEPTOR: { // 全局拦截器
          this.useGlobalInterceptors(...instanceMap.values())
          break;
        }
        case APP_GUARD: { // 全局守卫
          this.useGlobalGuard(...instanceMap.values())
          break;
        }
        case APP_PIPE: { // 全局管道
          this.useGlobalPipes(...instanceMap.values())
          break;
        }
        case APP_FILTER: { // 全局异常过滤器
          this.useGlobalFilters(...instanceMap.values())
          break;
        }
      }

    }
  }

  /**
   * 启动 HTTP 服务并监听端口
   * @param port 端口
   */
  async listen(port: number) {
    await this.initProviders() // 处理根模块树的提供者
    await this.initMiddlewares() // 初始化中间件
    await this.initGlobalProviders() // 初始化特殊的提供者
    await this.initController(this.module) // 初始化控制器 将路由处理函数与路由映射
    // 使用 express 中的 listen 方法启动 http 服务监听端口
    this.app.listen(port, () => {
      Logger.log(`Application is running on http://localhost:${port}`, 'NestApplication')
    })
  }
}



/**
 * 在生成HTTP请求路径时 path.posix.join 调用时 传递的第一个参数永远都是 '/'
 * path.posix.join 可以将
 * path.posix.join('/', '/') 转换成 '/'
 * path.posix.join('/', '/abc') 转换成 '/abc'
 * path.posix.join('/', 'abc') 转换成 '/abc'
 * path.posix.join('/', '/', 'abc') 转换成 '/abc'
 * path.posix.join('/', '/', '/abc') 转换成 '/abc'
 * path.posix.join('/', ':id') 转换成 '/:id'
 * path.posix.join('/cat', ':id') 转换成 '/cat/:id'
 * path.posix.join('/cat', '/:id') 转换成 '/cat/:id'
 * path.posix.join('/cat////', '/:id///') 转换成 '/cat/:id/'
 */
